#include <pthread.h>
#include <stdio.h>
#include "ring_buf.h"

#define ring_log printf
#define ATTR_IRAM 

//pthread_spinlock_t lock;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

static void lock(ring_buf *ring)
{
    pthread_mutex_lock(&mutex);
}

static void unlock(ring_buf *ring)
{
    pthread_mutex_unlock(&mutex);
}

void ring_buf_init(ring_buf *ring, void* buf, size_t size)
{
    ring->vol = size;
    ring->in = 0;
    ring->ou = 0;
    ring->buf = buf;
    ring->full = 0;
}

size_t ring_buf_size_vol(ring_buf *ring)
{
    return ring->vol;
}

size_t ring_buf_size_data(ring_buf *ring)
{
    size_t data;

    lock(ring);
    if(ring->in == ring->ou)
    {
        data = ring->full ? ring->vol : 0;
    }
    else if(ring->in > ring->ou)
    {
        data = ring->in - ring->ou;
    }
    else
    {
        data = ring->in + (ring->vol - ring->ou);
    }
    unlock(ring);
    return data;
}

size_t ring_buf_size_empt(ring_buf *ring)
{
    return ring_buf_size_vol(ring) - ring_buf_size_data(ring);
}

size_t ring_buf_push(ring_buf *ring, const void* buf, size_t size)
{
    size_t in, left;
    const uint8_t* src = (const uint8_t *)buf;

    //printf("push %d\n", size);
    left = ring_buf_size_empt(ring);
    if(size > left)        size = left;

    in  = ring->in;
    left = size;
    while(left--)
    {
        ring->buf[in] = *src++;
        in++;
        if(in >= ring->vol) in = 0;
    }

    lock(ring);
    ring->in = in;
    if (ring->in == ring->ou) ring->full = 1;
    unlock(ring);
    //printf("push over in:%d ou:%d\n", ring->in, ring->ou);
    return size;
}

/*
 * buf:
 *  如果为NULL,就只消耗ring中的数据
 * retval:
 * 实际pop的数据量
 */
size_t ring_buf_shift(ring_buf *ring, void* buf, size_t size)
{
    size_t ou, len;
    uint8_t *sptr;
    uint8_t* dst = (uint8_t *)buf;

    //printf("shift %d\n", size);
    len = ring_buf_size_data(ring);
    if(size > len)       size = len;

    ou = ring->ou;
    len = size;
    while(len--)
    {
        *dst++ = ring->buf[ou];
        ou++;
        if(ou >= ring->vol) ou = 0;
    }

    lock(ring);
    ring->ou = ou;
    if (ring->in == ring->ou) {
        ring->full = 0;
        ring->in = ring->ou = 0;
    }
    unlock(ring);
    //printf("shift over in:%d ou:%d\n", ring->in, ring->ou);
    return size;
}

void ring_buf_deinit(ring_buf *ring)
{
}

#if 0
#include <stdio.h>
#include <stdlib.h>
static void assert(int cond, char *msg, int line)
{
    if (!cond) {
        printf("%s line:%d\n", msg, line);
        abort();
    }
}
void ring_buf_test(void)
{
    size_t rev;
    static uint8_t buf1[10], buf2[10];
    static uint8_t buf[5];
    ring_buf ring;
    ring_buf_init(&ring, buf, sizeof(buf));

    assert(ring_buf_size_data(&ring) == 0, "", __LINE__);
    assert(ring_buf_size_empt(&ring) == 5, "", __LINE__);
    rev = ring_buf_shift(&ring, buf2, 1);
    assert(rev == 0, "", __LINE__);

    buf1[0] = 'A';
    buf1[1] = 'B';
    buf1[2] = 'C';
    buf1[3] = 'D';
    buf1[4] = 'E';
    buf1[5] = '1';
    buf1[6] = '2';
    buf1[7] = '7';
    buf1[8] = '8';
    rev = ring_buf_push(&ring, buf1, 1);
    assert(ring_buf_size_data(&ring) == 1, "", __LINE__);
    assert(ring_buf_size_empt(&ring) == 4, "", __LINE__);
    assert(rev == 1, "", __LINE__);
    rev = ring_buf_shift(&ring, buf2, 1);
    assert(rev == 1 && buf2[0] == 'A', "", __LINE__);
    rev = ring_buf_shift(&ring, buf2, 1);
    assert(rev == 0, "", __LINE__);

    rev = ring_buf_push(&ring, buf1, 2);
    assert(ring_buf_size_data(&ring) == 2, "", __LINE__);
    assert(ring_buf_size_empt(&ring) == 3, "", __LINE__);
    assert(rev == 2, "", __LINE__);
    memset(buf2, 0, sizeof(buf2));
    rev = ring_buf_shift(&ring, buf2, 3);
    assert(rev == 2 && buf2[0]=='A' && buf2[1]=='B', "", __LINE__);

    rev = ring_buf_push(&ring, buf1, 1);
    rev = ring_buf_push(&ring, buf1 + 1, 1);
    assert(ring_buf_size_data(&ring) == 2, "", __LINE__);
    assert(ring_buf_size_empt(&ring) == 3, "", __LINE__);
    assert(rev == 1, "", __LINE__);
    memset(buf2, 0, sizeof(buf2));
    rev = ring_buf_shift(&ring, buf2, 2);
    assert(rev == 2 && buf2[0]=='A' && buf2[1]=='B', "", __LINE__);

    rev = ring_buf_push(&ring, buf1, 5);
    assert(ring_buf_size_data(&ring) == 5, "", __LINE__);
    assert(ring_buf_size_empt(&ring) == 0, "", __LINE__);
    assert(rev == 5, "", __LINE__);
    ring_buf_shift(&ring, buf2, 2);
    rev = ring_buf_push(&ring, buf1 + 5, 4);
    assert(rev == 2, "", __LINE__);
    memset(buf2, 0, sizeof(buf2));
    rev = ring_buf_shift(&ring, buf2, 4);
    printf("rev:%d buf2:%s\n", rev, buf2);
    assert(rev == 4, "", __LINE__);
    memset(buf2, 0, sizeof(buf2));
    rev = ring_buf_shift(&ring, buf2, 4);
    printf("rev:%d buf2:%s\n", rev, buf2);
    assert(rev == 1, "", __LINE__);
    
    printf("Test over\n");
    exit(0);
}
#endif
